/*
* Copyright 2011 Pavel Kozhin 
*
* Licensed under the Apache License, Version 2.0 (the "License"); 
* you may not use this file except in compliance with the License. 
* You may obtain a copy of the License at 
* 
* http://www.apache.org/licenses/LICENSE-2.0
* 
* Unless required by applicable law or agreed to in writing, software 
* distributed  under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
* See the License for the specific language governing permissions and 
* limitations under the License.
*
* Author: Pavel Kozhin.
*/
package org.etherframework.core.bridge
{
	import flash.display.DisplayObject;
	import flash.errors.IllegalOperationError;
	import flash.system.ApplicationDomain;
	
	import org.etherframework.IEther;
	import org.etherframework.common.Access;
	import org.etherframework.common.DomainAccess;
	import org.etherframework.common.command.facade.ICommandsFacade;
	import org.etherframework.common.dependency.facade.IDependenciesFacade;
	import org.etherframework.common.mediation.facade.IMediationsFacade;
	import org.etherframework.common.mediators.facade.IMediatorsFacade;
	import org.etherframework.common.wire.IMetaProcessor;
	import org.etherframework.common.wire.IWirable;
	import org.etherframework.common.wire.IWire;
	import org.etherframework.core.IModule;
	import org.etherframework.core.context.Context;
	import org.etherframework.core.context.Default;
	import org.etherframework.core.context.IContexts;
	import org.etherframework.core.manager.DestructionManager;
	import org.etherframework.core.module.IModulePartFacade;
	import org.etherframework.core.utils.Dispatcher;
	import org.etherframework.dependency.Dependency;
	import org.etherframework.mediator.IMediator;
	import org.etherframework.mediator.IMediators;
	
	[ExcludeClass]
	
	// TODO: Refactor.
	public final class GlobalFacade implements IBridgeFacades, IDependenciesFacade, 
		IMediatorsFacade
	{
		private var _contexts:Vector.<IModule>;
		private var _applicationDomain:ApplicationDomain;
		private var _context:Context;
		
		public function GlobalFacade(contexts:Vector.<IModule>, context:Context,
									 applicationDomain:ApplicationDomain)
		{
			if (!contexts)
				throw new ArgumentError("Argument contexts:Vector.<IContext> can not be null.");
			if (!context)
				throw new ArgumentError("Argument context:Context can not be null.");
			if (!applicationDomain)
				throw new ArgumentError("Argument applicationDomain:ApplicationDomain can not be null.");
			
			_contexts = contexts;
			_context = context;
			_applicationDomain = applicationDomain;
		}
		
		public function getInstance(asked:Class):*
		{
			var result:*;
			var dependency:Dependency;
			
			for each (var item:IModule in _contexts)
			{
				if (item.access == Access.GLOBAL && 
					((item.domain == DomainAccess.SINGLE && 
						_applicationDomain == item.applicationDomain) || 
						item.domain == DomainAccess.MULTIPLE))
				{
					dependency = item.dependencies.contextual(_context).getDependency(asked);
					
					if (dependency)
						result = dependency.getInstance();
					
					if (result)
						return result;
				}
			}
			
			return null;
		}
		
		public function getWire(target:Object):IWire
		{
			var result:IWire;
			
			for each (var item:IModule in _contexts)
			{
				if (item.access == Access.GLOBAL && 
					((item.domain == DomainAccess.SINGLE && 
						_applicationDomain == item.applicationDomain) || 
						item.domain == DomainAccess.MULTIPLE))
				{
					result = item.wiring.hasWire(target);
					
					if (result)
						return result;
				}
			}
			
			return null;
		}
		
		public function wire(target:IWirable, ether:IEther):IWire
		{
			if (!target)
				return null;
			
			const wire:IWire = getWire(target);
			
			if (wire)
			{
				if (!wire.ether)
					wire.initialize(ether);
				if (!wire.wired)
					wire.wire();
			}
			return wire;
		}
		
		public function unwire(target:IWirable):IWire
		{
			if (!target)
				return null;
			
			const wire:IWire = getWire(target);
			
			if (wire && wire.wired)
			{
				wire.unwire();
			}
			
			return wire;
		}
		
		public function createProcessor(metatag:String):IMetaProcessor
		{
			var result:IMetaProcessor;
			
			for each (var item:IModule in _contexts)
			{
				if (item.access == Access.GLOBAL && 
					((item.domain == DomainAccess.SINGLE && 
						_applicationDomain == item.applicationDomain) || 
						item.domain == DomainAccess.MULTIPLE))
				{
					result = item.wiring.createProcessor(metatag);
					
					if (result)
						return result;
				}
			}
			
			return null;
		}
		
		public function createMediator(view:DisplayObject):IMediator
		{
			var result:IMediator;
			var mediators:IMediators;
			
			for each (var item:IModule in _contexts)
			{
				if (item.access == Access.GLOBAL && 
					((item.domain == DomainAccess.SINGLE && 
						_applicationDomain == item.applicationDomain) || 
						item.domain == DomainAccess.MULTIPLE))
				{
					mediators = item.mediators.contextual(_context); 
					result = mediators.hasMediatorMapping(view["constructor"]) ? 
						mediators.createMediator(view) :  null;
					
					if (result)
						return result;
				}
			}
			
			return null;
		}
		
		public function hasMediator(view:DisplayObject):IMediator
		{
			var result:IMediator;
			
			for each (var item:IModule in _contexts)
			{
				if (item.access == Access.GLOBAL && 
					((item.domain == DomainAccess.SINGLE && 
						_applicationDomain == item.applicationDomain) || 
						item.domain == DomainAccess.MULTIPLE))
				{
					result = item.mediators.hasMediator(view);
					
					if (result)
						return result;
				}
			}
			
			return null;
		}
		
		public function destroyMediator(view:DisplayObject):IMediator
		{
			var result:IMediator;
			
			for each (var item:IModule in _contexts)
			{
				if (item.access == Access.GLOBAL && 
					((item.domain == DomainAccess.SINGLE && 
						_applicationDomain == item.applicationDomain) || 
						item.domain == DomainAccess.MULTIPLE))
				{
					result = item.mediators.destroyMediator(view);
					
					if (result)
						return result;
				}
			}
			
			return null;
		}
		
		public function hasMediatorMapping(view:Class):Boolean
		{
			var result:Boolean = false;
			var mediators:IMediators;
			
			for each (var item:IModule in _contexts)
			{
				if (item.access == Access.GLOBAL && 
					((item.domain == DomainAccess.SINGLE && 
						_applicationDomain == item.applicationDomain) || 
						item.domain == DomainAccess.MULTIPLE))
				{
					mediators = item.mediators.contextual(_context);
					result = mediators.hasMediatorMapping(view);
					
					if (result)
						return result;
				}
			}
			
			return result;
		}
		
		public function prepare(ether:IEther):IModulePartFacade 
		{
			throw new IllegalOperationError("Method is not allowed.");
		}
		
		public function contextual(context:Context):IBridgeFacades
		{
			// Can't use Contexts due to changing application domain.
			return context.isType(Default) ? this : 
				new GlobalFacade(_contexts, context, _applicationDomain);
		}
		
		public function destroy():void
		{
		}
		
		public function get dependencies():IDependenciesFacade { return this; }
		public function get mediators():IMediatorsFacade { return this; }
		public function get commands():ICommandsFacade { return null; }
		public function get mediations():IMediationsFacade { return null; }
		
		public function get parent():IBridgeFacades { return null; };
		public function get context():Context { return _context; }
		public function get domain():String { return DomainAccess.MULTIPLE; }
		public function get access():String { return Access.GLOBAL; }
		
		public function get destructionManager():DestructionManager { return null; }
		public function get dispatcher():Dispatcher { return null; }
		public function get controllers():Controllers { return null; }
		public function get contexts():IContexts { return null; }
	}
}